package com.mdd.common.util;

import com.mdd.common.config.WxPayConfig;
import com.mdd.common.core.pay.WxPrePayForm;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.core.notification.NotificationParser;
import com.wechat.pay.java.core.notification.RequestParam;
import com.wechat.pay.java.service.payments.jsapi.JsapiServiceExtension;
import com.wechat.pay.java.service.payments.jsapi.model.*;
import com.wechat.pay.java.service.payments.jsapi.JsapiService;
import com.wechat.pay.java.service.payments.model.Transaction;
import com.wechat.pay.java.service.refund.RefundService;
import com.wechat.pay.java.service.refund.model.CreateRequest;
import com.wechat.pay.java.service.refund.model.QueryByOutRefundNoRequest;
import com.wechat.pay.java.service.refund.model.Refund;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.Objects;


/**
 * @author shenjian
 * @createTime 2023年02月11日 10:14:00
 */
@Component
public class WxPayUtils {

    private final static String WECHATPAY_SIGNATURE = "Wechatpay-Signature";
    private final static String WECHATPAY_NONCE = "Wechatpay-Nonce";
    private final static String WECHATPAY_TIMESTAMP = "Wechatpay-Timestamp";
    private final static String WECHATPAY_SERIAL = "Wechatpay-Serial";
    private final static String WECHATPAY_SIGNATURE_TYPE = "Wechatpay-Signature-Type";

    private static WxPayConfig wxPayConfig;
    private static JsapiService wxJsapiService;
    private static RefundService refundService;
    private static RSAAutoCertificateConfig wxCertificateConfig;
    private static JsapiServiceExtension jsapiServiceExtension;

    @Resource
    public void setWxJsapiService(JsapiService wxJsapiService) {
        WxPayUtils.wxJsapiService = wxJsapiService;
    }
    @Resource
    public void setRefundService(RefundService refundService) {
        WxPayUtils.refundService = refundService;
    }
    @Resource
    public void setWxPayConfig(WxPayConfig wxPayConfig) {
        WxPayUtils.wxPayConfig = wxPayConfig;
    }
    @Resource
    public void setWxCertificateConfig(RSAAutoCertificateConfig wxCertificateConfig) {
        WxPayUtils.wxCertificateConfig = wxCertificateConfig;
    }
    @Resource
    public void setJsapiServiceExtension(JsapiServiceExtension jsapiServiceExtension) {
        WxPayUtils.jsapiServiceExtension = jsapiServiceExtension;
    }

    /**
     * 预支付请求发起
     *
     * @param form 预支付表单
     * @return
     */
    public static PrepayWithRequestPaymentResponse requestWxPrePay(WxPrePayForm form) {
        PrepayRequest prepayRequest = new PrepayRequest();
        // 应用ID
        prepayRequest.setAppid(wxPayConfig.getAppId());
        // 直连商户号
        prepayRequest.setMchid(wxPayConfig.getMerchantId());
        // 商品描述
        prepayRequest.setDescription(form.getDescription());
        // 通知地址
        prepayRequest.setNotifyUrl(wxPayConfig.getPayNotifyDomain());
        // 系统内部商户订单号
        prepayRequest.setOutTradeNo(form.getOutTradeNo());
        // 附件数据
        if (StringUtils.isNotEmpty(form.getAttach())) {
            prepayRequest.setAttach(form.getAttach());
        }
        // 金额
        Amount amount = new Amount();
        amount.setTotal(form.getAmount());
        prepayRequest.setAmount(amount);
        // 支付者
        Payer payer = new Payer();
        payer.setOpenid(form.getOpenId());
        prepayRequest.setPayer(payer);
        // jar api
        return jsapiServiceExtension.prepayWithRequestPayment(prepayRequest);
    }

    /**
     * 微信回调处理类
     *
     * @param headers      请求头
     * @param requestBody  请求体
     * @param clazz        返回类型
     * @param <T>
     */
    public static <T> T wxCallback(HttpHeaders headers,
                                   String requestBody,
                                   Class<T> clazz) {
        String signature = Objects.requireNonNull(headers.get(WECHATPAY_SIGNATURE)).get(0);
        String nonce = Objects.requireNonNull(headers.get(WECHATPAY_NONCE)).get(0);
        String timestamp = Objects.requireNonNull(headers.get(WECHATPAY_TIMESTAMP)).get(0);
        String serial = Objects.requireNonNull(headers.get(WECHATPAY_SERIAL)).get(0);
        String signType = Objects.requireNonNull(headers.get(WECHATPAY_SIGNATURE_TYPE)).get(0);
        RequestParam requestParam = new RequestParam.Builder()
                .signature(signature)
                .serialNumber(serial)
                .nonce(nonce)
                .timestamp(timestamp)
                .signType(signType)
                .body(requestBody)
                .build();
        NotificationParser parser = new NotificationParser(wxCertificateConfig);
        return parser.parse(requestParam, clazz);
    }

    /**
     * 查询订单详情
     *
     * @param transactionId 微信支付订单号
     * @return
     */
    public static Transaction queryOrder(String transactionId) {
        QueryOrderByIdRequest queryRequest = new QueryOrderByIdRequest();
        queryRequest.setMchid(wxPayConfig.getMerchantId());
        queryRequest.setTransactionId(transactionId);
        return wxJsapiService.queryOrderById(queryRequest);
    }

    /**
     * 根据订单号查询订单详情
     *
     * @param orderNo 订单号
     * @return
     */
    public static Transaction queryOrderByNo(String orderNo) {
        QueryOrderByOutTradeNoRequest request = new QueryOrderByOutTradeNoRequest();
        request.setMchid(wxPayConfig.getMerchantId());
        request.setOutTradeNo(orderNo);
        return wxJsapiService.queryOrderByOutTradeNo(request);
    }

    /**
     * 关闭订单
     *
     * @param outTradeNo 内部系统订单号
     */
    public static void closeOrder(String outTradeNo) {
        CloseOrderRequest closeRequest = new CloseOrderRequest();
        closeRequest.setMchid(wxPayConfig.getMerchantId());
        closeRequest.setOutTradeNo(outTradeNo);
        wxJsapiService.closeOrder(closeRequest);
    }

    /**
     * 微信退款
     * @param outTradeNo
     * @return Refund
     */
    public static Refund refund(String outTradeNo) {
        QueryByOutRefundNoRequest queryRequest = new QueryByOutRefundNoRequest();
        queryRequest.setOutRefundNo(outTradeNo);
        return refundService.queryByOutRefundNo(queryRequest);
    }

    /**
     * 申请微信退款
     * @param refundRequest
     * @return Refund
     */
    public static Refund applyRefund(CreateRequest refundRequest) {
        refundRequest.setNotifyUrl(wxPayConfig.getRefundNotifyDomain());
        return refundService.create(refundRequest);
    }


}
